spb/cancerindex
Public
TypeScript 97.2%
SQL 1.5%
CSS 0.6%
JavaScript 0.5%
1import type { Metadata } from 'next';2import { notFound, permanentRedirect } from 'next/navigation';3import { loadCancer, TABS, type TabKey } from '@/components/cancer/load';4import { CancerHeader } from '@/components/cancer/header';5import { OverviewTab } from '@/components/cancer/tabs/overview';6import { StatisticsTab } from '@/components/cancer/tabs/statistics';7import { SurvivalTab } from '@/components/cancer/tabs/survival';8import { GenomicsTab } from '@/components/cancer/tabs/genomics';9import { EvidenceTab } from '@/components/cancer/tabs/evidence';10import { DrugsTab } from '@/components/cancer/tabs/drugs';11import { TrialsTab } from '@/components/cancer/tabs/trials';12import { ResearchTab } from '@/components/cancer/tabs/research';13import { RankingsTab } from '@/components/cancer/tabs/rankings';14import { SourcesTab } from '@/components/cancer/tabs/sources';15import { jsonLd, medicalConditionLd } from '@/lib/seo';16import { str, int, type SP } from '@/lib/search-params';17import { truncate } from '@/lib/format';1819export const revalidate = 3600;20export const dynamicParams = true;2122type Params = { slug: string; tab?: string[] };2324function tabKey(tab: string[] | undefined): TabKey | null {25 const t = tab?.[0] ?? 'overview';26 if (tab && tab.length > 1) return null;27 return TABS.some((x) => x.key === t) ? (t as TabKey) : null;28}2930export async function generateMetadata({ params }: { params: Promise<Params> }): Promise<Metadata> {31 const { slug, tab } = await params;32 const b = await loadCancer(slug);33 if (!b) return { title: 'Not found' };34 const key = tabKey(tab) ?? 'overview';35 const label = TABS.find((t) => t.key === key)?.label ?? '';36 const desc = b.cancer.description ? truncate(b.cancer.description, 160) : `${b.cancer.canonical_name} (${b.cancer.id}): taxonomy, statistics, genomics, evidence, drugs, trials, literature and rankings with full provenance.`;37 return {38 title: key === 'overview' ? b.cancer.canonical_name : `${b.cancer.canonical_name} — ${label}`,39 description: desc,40 alternates: { canonical: key === 'overview' ? `/cancer/${b.cancer.slug}` : `/cancer/${b.cancer.slug}/${key}` },41 };42}4344export default async function CancerPage({ params, searchParams }: { params: Promise<Params>; searchParams: Promise<SP> }) {45 const { slug, tab } = await params;46 const key = tabKey(tab);47 if (!key) notFound();48 const b = await loadCancer(slug);49 if (!b) notFound();50 if (b.cancer.slug !== slug) permanentRedirect(key === 'overview' ? `/cancer/${b.cancer.slug}` : `/cancer/${b.cancer.slug}/${key}`);5152 let body: React.ReactNode;53 switch (key) {54 case 'statistics':55 body = <StatisticsTab b={b} />;56 break;57 case 'survival':58 body = <SurvivalTab b={b} />;59 break;60 case 'genomics': {61 const sp = await searchParams;62 body = <GenomicsTab b={b} cohort={str(sp, 'cohort') || null} />;63 break;64 }65 case 'evidence': {66 const sp = await searchParams;67 body = <EvidenceTab b={b} evPage={int(sp, 'evPage', 1, 1, 100_000)} />;68 break;69 }70 case 'drugs': {71 const sp = await searchParams;72 body = <DrugsTab b={b} jurisdiction={str(sp, 'jurisdiction') || null} aPage={int(sp, 'aPage', 1, 1, 100_000)} />;73 break;74 }75 case 'trials': {76 const sp = await searchParams;77 body = <TrialsTab b={b} status={str(sp, 'status')} phase={str(sp, 'phase')} page={int(sp, 'page', 1, 1, 100_000)} />;78 break;79 }80 case 'research': {81 const sp = await searchParams;82 body = <ResearchTab b={b} pPage={int(sp, 'pPage', 1, 1, 100_000)} />;83 break;84 }85 case 'rankings':86 body = <RankingsTab b={b} />;87 break;88 case 'sources':89 body = <SourcesTab b={b} />;90 break;91 default:92 body = <OverviewTab b={b} />;93 }9495 const ld = medicalConditionLd({ slug: b.cancer.slug, canonicalName: b.cancer.canonical_name, description: b.cancer.description, aliases: b.aliases.filter((a) => a.alias_type !== 'preferred').map((a) => a.alias), codes: b.codes });9697 return (98 <article>99 <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: jsonLd(ld) }} />100 <CancerHeader b={b} tab={key} />101 <div className="pt-6">{body}</div>102 </article>103 );104}105